#include "loraservice.h"
#include <configure.h>
#include <chrono>
#include <loradevice.h>
#include <oiltrans.h>
#include <logger/logger.h>
#include <lora_protocol_v1.h>
#include <cassert>
#include <resources.h>

#define HAS_LORA 0

namespace cores
{
    namespace services
    {
        namespace v1
        {
            using namespace cores::common::configures;
            using namespace cores::devices;
            using namespace cores::database::terminal;
            using namespace cores::protocols::lora_v1;
            using namespace cores::common;

            LoraStatus::LoraStatus(const uint16_t &loraId,
                                   const uint32_t &expireTime)
                : _loraId(loraId), _expireTime(expireTime)

            {
            }

            LoraStatus::~LoraStatus()
            {
            }

            uint16_t LoraStatus::loraId() const
            {
                return this->_loraId;
            }

            bool LoraStatus::timeIsExpired() const
            {
                return chrono::duration_cast<chrono::seconds>(LoraStatus::steadyNow() - _updateTime).count() >= _expireTime;
            }

            uint32_t LoraStatus::getExpiredTime() const
            {
                return chrono::duration_cast<chrono::seconds>(LoraStatus::steadyNow() - _updateTime).count() - _expireTime;
            }

            uint32_t LoraStatus::expireTime() const
            {
                return this->_expireTime;
            }

            void LoraStatus::updateTime(const chrono::steady_clock::time_point &updateTime)
            {
                _updateTime = updateTime;
            }

            uint32_t LoraStatus::now()
            {
                return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            }

            chrono::steady_clock::time_point LoraStatus::steadyNow()
            {
                return chrono::steady_clock::now();
            }

            int LoraStatus::toBuffer(uint8_t *buffer, const int &size)
            {
                buffer[0] = _loraId >> 8;
                buffer[1] = _loraId & 0xff;
                int result{0};

                shared_ptr<LoraProtocol> protocol;

                if (_neededDownToLora)
                {
                    vector<LoraParam> params;
                    auto devices = LoraDeviceManager::GetOilTrans(_loraId);
                    for (auto &device : devices)
                    {
                        params.push_back({device->nozzle_code, device->category, device->screen_type});
                    }
                    protocol = shared_ptr<LoraProtocol>(new DownLoraParamProtocol(_loraId, params, LoraStatus::now()));
                }
                else
                {
                    protocol = shared_ptr<LoraProtocol>(new ReadLoraParamProtocol(_loraId, LoraStatus::now()));
                }

                result = protocol->toBuffer(buffer + 2, size - 2);
                if (result > 0)
                    return result + 2;

                return result;
            }

            bool LoraStatus::configIsOk() const
            {
                return _configIsOk &&
                       chrono::duration_cast<chrono::seconds>(chrono::steady_clock::now() - _updateTime).count() < _expireTime;
            }

            void LoraStatus::updateConfig(const bool &ok)
            {
                _configIsOk = ok;
                // _updateTime = chrono::steady_clock::now();
            }

            bool LoraStatus::isMustDownload() const
            {
                return _neededDownToLora;
            }

            void LoraStatus::needToDownload(const bool &needed)
            {
                _neededDownToLora = needed;
            }

            /**
             * 下面是Lora状态管理类
             *
             */

            LoraStatusManager *LoraStatusManager::globalInstance{nullptr};

            LoraStatusManager::LoraStatusManager()
            {
                if (LoraStatusManager::globalInstance == nullptr)
                    LoraStatusManager::globalInstance = this;

                initialize();
            }

            LoraStatusManager::~LoraStatusManager()
            {
                if (LoraStatusManager::globalInstance == this)
                    LoraStatusManager::globalInstance = nullptr;
            }

            LoraStatusManager *LoraStatusManager::Instance()
            {
                return LoraStatusManager::globalInstance;
            }

            void LoraStatusManager::initialize()
            {
                // 初始化LORA设备状态
                auto loraIds = LoraDeviceManager::GetLoraIDVector();
                auto expiredTime = Configure::toInt("expired_time");
                for (auto &loraId : loraIds)
                {
                    add(loraId, expiredTime);
                }
            }

            void LoraStatusManager::Add(const uint16_t &loraId, const uint32_t &expiredTime)
            {
                assert(LoraStatusManager::globalInstance);
                LoraStatusManager::globalInstance->add(loraId, expiredTime);
            }

            void LoraStatusManager::add(const uint16_t &loraId, const uint32_t &expiredTime)
            {
                if (_statusMap.count(loraId) == 0)
                {
                    auto status = make_shared<LoraStatus>(loraId, expiredTime);
                    _status.push_back(status.get());
                    _statusMap.insert(make_pair(loraId, status));
                }
            }

            bool LoraStatusManager::ConfigIsOk(const uint16_t &loraId)
            {
                assert(LoraStatusManager::globalInstance);
                return LoraStatusManager::globalInstance->configIsOk(loraId);
            }

            bool LoraStatusManager::configIsOk(const uint16_t &loraId)
            {
                // 确保该LoraID存在
                assert(_statusMap.count(loraId) > 0);
                return _statusMap[loraId]->configIsOk();
            }

            void LoraStatusManager::UpdateTime(
                const uint16_t &loraId,
                const chrono::steady_clock::time_point &updateTime_)
            {
                assert(LoraStatusManager::globalInstance);
                LoraStatusManager::globalInstance->updateTime(loraId, updateTime_);
            }

            void LoraStatusManager::updateTime(
                const uint16_t &loraId,
                const chrono::steady_clock::time_point &updateTime_)
            {
                // 确保该LoraID存在
                assert(_statusMap.count(loraId) > 0);
                _statusMap[loraId]->updateTime(updateTime_);
            }

            void LoraStatusManager::UpdateConfig(const uint16_t &loraId, const bool &ok)
            {
                assert(LoraStatusManager::globalInstance);
                LoraStatusManager::globalInstance->updateConfig(loraId, ok);
            }

            void LoraStatusManager::updateConfig(const uint16_t &loraId, const bool &ok)
            {
                // 确保该LoraID存在
                assert(_statusMap.count(loraId) > 0);
                _statusMap[loraId]->updateConfig(ok);
            }

            int LoraStatusManager::ToBuffer(uint8_t *buffer, const int &size)
            {
                assert(LoraStatusManager::globalInstance);
                return LoraStatusManager::globalInstance->toBuffer(buffer, size);
            }

            int LoraStatusManager::toBuffer(uint8_t *buffer, const int &size)
            {
                shared_ptr<LoraProtocol> protocol;
                uint16_t loraId{0};
                for (auto &status : _status)
                {
                    loraId = status->loraId();
                    if (status->timeIsExpired())
                    {
                        XLOG("1");
                        if (status->isMustDownload())
                        {
                            XLOG("2");
                            vector<LoraParam> params;
                            auto devices = LoraDeviceManager::GetOilTrans(loraId);
                            for (auto &device : devices)
                            {
                                params.push_back({device->nozzle_code, device->category, device->screen_type});
                            }
                            protocol = shared_ptr<LoraProtocol>(new DownLoraParamProtocol(loraId, params, LoraStatus::now()));
                        }
                        else
                        {
                            protocol = shared_ptr<LoraProtocol>(new ReadLoraParamProtocol(loraId, LoraStatus::now()));
                        }
                        status->updateTime();
                        break;
                    }
                }
                if (protocol.get() == nullptr)
                    return 0;

                buffer[0] = loraId >> 8;
                buffer[1] = loraId & 0xff;
                return protocol->toBuffer(buffer + 2, size - 2);
            }

            void LoraStatusManager::MustDownLoadConfig(const uint16_t &loraId, const bool &needed)
            {
                assert(LoraStatusManager::globalInstance);
                LoraStatusManager::globalInstance->mustDownLoadConfig(loraId, needed);
            }

            void LoraStatusManager::mustDownLoadConfig(const uint16_t &loraId, const bool &needed)
            {
                if (_statusMap.count(loraId) > 0)
                    _statusMap[loraId]->needToDownload(needed);
            }

            /**
             * 下面的代码管理流水号
             *
             */

            SerialNumberManager *SerialNumberManager::globalInstance{nullptr};

            SerialNumberManager::SerialNumberManager()
            {
                if (SerialNumberManager::globalInstance == nullptr)
                {
                    initialize();
                    SerialNumberManager::globalInstance = this;
                }
            }

            SerialNumberManager::~SerialNumberManager()
            {
                for (auto &data : _dataMap)
                {
                    data.second.reset();
                }
                _dataMap.clear();

                if (SerialNumberManager::globalInstance == this)
                    SerialNumberManager::globalInstance = nullptr;
            }

            SerialNumber *SerialNumberManager::getObject(const uint16_t &loraId, const uint8_t &nozzle)
            {
                auto key = hashKey(loraId, nozzle);
                if (_dataMap.count(key) == 0)
                    return nullptr;

                return _dataMap[key].get();
            }

            bool SerialNumberManager::updateTransSerialNumber(const uint16_t &loraId, const uint8_t &nozzle, const uint16_t &sn)
            {
                auto obj = getObject(loraId, nozzle);
                if (obj == nullptr || obj->lora_id != loraId || obj->nozzle_code != nozzle)
                    return false;

                obj->trans_max_num = sn;
                OilTrans::updateSerialNumberRecordS(*obj);
                return true;
            }

            bool SerialNumberManager::updateTaxSerialNumber(const uint16_t &loraId, const uint8_t &nozzle, const uint16_t &sn)
            {
                auto obj = getObject(loraId, nozzle);
                if (obj == nullptr || obj->lora_id != loraId || obj->nozzle_code != nozzle)
                    return false;

                obj->tax_max_num = sn;

                OilTrans::updateSerialNumberRecordS(*obj);
                return true;
            }

            void SerialNumberManager::print()
            {
                for (auto iter = _dataMap.begin(); iter != _dataMap.end(); iter++)
                {
                    iter->second->print();
                }
            }

            SerialNumberManager *SerialNumberManager::Instance()
            {
                return SerialNumberManager::globalInstance;
            }

            uint32_t SerialNumberManager::hashKey(const uint16_t &first, const uint8_t &second)
            {
                return (first << 8) + second;
            }

            void SerialNumberManager::initialize()
            {
                auto n = LoraDeviceManager::Count();
                for (auto i = 0; i < n; i++)
                {
                    auto data = LoraDeviceManager::Get(i);
                    if (data != nullptr)
                    {
                        auto sn = OilTrans::getSerialNumberDataS(data->lora_id, data->nozzle_code);
                        shared_ptr<SerialNumber> tempSn;
                        if (sn.empty())
                        {
                            // 新增序号
                            tempSn = make_shared<SerialNumber>(data->lora_id, data->nozzle_code, 0, 0);

                            // 添加到数据库
                            OilTrans::addSerialNumberRecordS(*tempSn);
                        }
                        else
                        {
                            tempSn = make_shared<SerialNumber>(sn[0]);
                        }
                        //
                        _dataMap[hashKey(data->lora_id, data->nozzle_code)] = tempSn;
                    }
                }
            }

            LoraTokenManager::LoraTokenManager() : _currentTokenIndex(0)
            {
            }

            LoraTokenManager::~LoraTokenManager()
            {
            }

            int LoraTokenManager::toBuffer(uint8_t *buffer, const int &size)
            {
                auto n = LoraDeviceManager::Count();
                if (_currentTokenIndex >= n)
                    _currentTokenIndex -= n;

                auto device = LoraDeviceManager::Get(_currentTokenIndex);
                if (device == nullptr)
                    XERROR("has no device of index = %d", _currentTokenIndex);

                _currentTokenIndex++;
                assert(device);

#if 0
                if (device->is_config_error)
                    return 0;
#else
                if (LoraStatusManager::ConfigIsOk(device->lora_id) == false)
                    return 0;
#endif
                auto now_time = LoraStatus::now();
                auto manager = SerialNumberManager::Instance();
                auto sn = manager->getObject(device->lora_id, device->nozzle_code);
                if (sn == nullptr)
                    XERROR("has no serial number of lora_id = %d and nozzle_code = %d", device->lora_id, device->nozzle_code);
                assert(sn);

                // 先添加LoraId
                buffer[0] = device->lora_id >> 8;
                buffer[1] = device->lora_id & 0xff;

                TransTokenLoraProtocol protocl{device->lora_id, device->nozzle_code, now_time, sn->trans_max_num};
                auto result = protocl.toBuffer(buffer + 2, size - 2);
                if (result > 0)
                {
                    // XLOG("lora trans token: %s", Resources::bytesToHexStr(buffer, result + 2).c_str());
                    return result + 2;
                }

                return 0;
            }

            LoraService::LoraService(const string &serviceName)
                : BaseService(serviceName), Publisher("lora service")
            {
            }

            LoraService::~LoraService()
            {
            }

            void LoraService::init()
            {
                // 初始化Lora设备状态管理对象
                _loraStatus = make_shared<LoraStatusManager>();

                // 初始化流水号管理对象
                _snManger = make_shared<SerialNumberManager>();

                // 初始化令牌协议管理对象
                _loraTokens = make_shared<LoraTokenManager>();
            }

            void LoraService::start()
            {
                if (_running)
                    return;

                    // 创建连接Lora的串口对象
#if HAS_LORA
                _loraDevice = make_shared<SerialPort>(
                    Configure::toString("dev_name"),
                    Configure::toInt("baud"),
                    Configure::toInt("data_bit"));

                _running = _loraDevice->isOpen();
#else
                _running = true;
#endif
                assert(_running);

                thread senderThread([this]
                                    { this->sendData(); });

                thread recverThread([this]
                                    { this->recvData(); });

                senderThread.detach();
                recverThread.detach();
            }

            void LoraService::stop()
            {
                BaseService::stop();
            }

            void LoraService::dealLoraToken()
            {
            }

            bool LoraService::dealLoraData()
            {
                return true;
            }

            void LoraService::sendData()
            {
                uint8_t buffer[128] = {0};

                while (_running)
                {
#if 0
                    // 通过Lora发送指令
                    if (Configure::toBool("is_update_config"))
                    {
                        // 当更新配置时
                        updateLora();
                        this_thread::sleep_for(chrono::milliseconds(1000));
                    }
                    else
                    {
                        // 发送Lora令牌
                        sendLoraToken();

                        this_thread::sleep_for(chrono::milliseconds(50));
                    }
#else
                    auto sendFlag = true;
                    auto nowTime = chrono::steady_clock::now();
                    if (_waitingForAnswer)
                    {
                        // 超时等待
                        if (chrono::duration_cast<chrono::milliseconds>(nowTime - _lastTime).count() < Configure::toInt("lora_expired_time"))
                            sendFlag = false;
                    }

                    auto result{0};
                    string caption = "读取Lora配置参数";
                    if (sendFlag)
                    {
                        // 是否需要检验Lora设备状态
                        result = LoraStatusManager::ToBuffer(buffer, 128);
                        if (result == 0)
                        {
                            result = _loraTokens->toBuffer(buffer, 128);
                            caption = "数据交易令牌";
                        }
                    }

#if HAS_LORA
                    if (result > 0 && _loraDevice->isOpen())
                    {
                        _loraDevice->writeData((const char *)buffer, result);
                        XLOG("%s: %s", caption.c_str(), Resources::bytesToHexStr(buffer, result).c_str());
                    }

#else
                    if (result > 0)
                        XLOG("%s: %s", caption.c_str(), Resources::bytesToHexStr(buffer, result).c_str());
                        // else
                        // XERROR("no send data");
#endif
                    if (result > 0)
                    {
                        _lastTime = nowTime;
                        _waitingForAnswer = true;
                    }

                    this_thread::sleep_for(chrono::milliseconds(50));
#endif
                }
            }

            void LoraService::recvData()
            {
                uint8_t dataBuffer[128] = {0};
                int dataPos{0};

                LoraUploadProtocolParser parser;

                while (_running)
                {
                    // 接收并处理Lora数据
#if HAS_LORA
                    auto bytesRead = _loraDevice->readData((unsigned char*)(dataBuffer + dataPos), 128 - dataPos);
                    dataPos += bytesRead;
                    if (dataPos > 3 && dataBuffer[2] != 0xfe)
                        dataPos = 0;

                    uint16_t len = dataBuffer[3] + dataBuffer[4] << 8;
                    if (dataPos >= len + 6)
                    {
                        if (parser.isChecked(dataBuffer + 2, dataPos - 2))
                        {
                            auto protocol = parser.parse(dataBuffer + 2, dataPos - 2);
                        }

                        dataPos = 0;
                    }
#endif

                    this_thread::sleep_for(chrono::milliseconds(50));
                }
            }

            int LoraService::updateLora()
            {
                // 更新Lora配置
                auto sendFlag = true;
                auto nowTime = chrono::steady_clock::now();
                if (_waitingForAnswer)
                {
                    // 超时等待
                    if (chrono::duration_cast<chrono::milliseconds>(nowTime - _lastTime).count() < Configure::toInt("lora_expired_time"))
                        sendFlag = false;
                }

                if (sendFlag)
                {
                    uint8_t buffer[128];
                }

                return 0;
            }

            void LoraService::sendLoraToken()
            {
                auto sendFlag = true;
                auto nowTime = chrono::steady_clock::now();
                if (_waitingForAnswer)
                {
                    // 当正在等待时
                    // 计算超时，如果不超时怎
                    if (chrono::duration_cast<chrono::milliseconds>(nowTime - _lastTime).count() < Configure::toInt("lora_expired_time"))
                        sendFlag = false;
                }

                if (sendFlag)
                {
                    // 则发送新的令牌
                    uint8_t buffer[128];
                    auto result = _loraTokens->toBuffer(buffer, 128);

#if HAS_LORA
                    if (result > 0 && _loraDevice->isOpen())
                        _loraDevice->writeData((const char *)buffer, result);
#else
                    if (result > 0)
                        XLOG("lora trans token: %s", Resources::bytesToHexStr(buffer, result).c_str());
#endif

                    _lastTime = nowTime;
                    _waitingForAnswer = true;
                }
            }
        }
    }
}
